#pragma once
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <utility>
#include <cassert>

#ifdef _WIN32
	#define IsEnv_PC true
#else
	#define IsEnv_PC false
#endif

#if defined(__linux__) || defined(__unix__)
	#define IsEnv_Linux true
#else
	#define IsEnv_Linux false
#endif

#ifdef __ANDROID__
	#define IsEnv_Android true
#else
	#define IsEnv_Android false
#endif

// hard-coded by CMakeLists.txt
#ifdef __QT__
	#define IsEnv_Qt true
#else
	#define IsEnv_Qt false
#endif

using UInt = unsigned int;
using UChar = unsigned char;
using UCharArray = std::vector<UChar>;

#define CByteArray UCharArray
#define CStringList CVector<CString>

#define InvalidNum 12321
#define EPSILON 1e-6f

#define DebugDestructor(ClassName) ~ClassName() { std::cout << __FUNCTION__ << std::endl; }

#ifdef __ANDROID__
	#include <android/log.h>
	#define ASSERT_PNT(cond, what) \
		__android_log_print(ANDROID_LOG_ERROR, "ASSERT", "Assertion failed in %s: %s (Condition: '%s')", __FUNCTION__, what, #cond);
	#define EXIT_PNT(what) \
		__android_log_print(ANDROID_LOG_ERROR, "EXIT", "Exit in %s: %s", __FUNCTION__, what);
	#define CHECK_GL_PNT(e, desc) \
		__android_log_print(ANDROID_LOG_ERROR, "CHECK_GL_ERR", "OpenGL error in function: %s, at line: %d; GLError{code, desc} = { %d, %s }", __FUNCTION__, __LINE__, e, desc);
#else
	#include <cstdio>
	#define ASSERT_PNT(cond, what) \
		std::cerr << "Assertion failed in " << __FUNCTION__ << ": " << what << " (Condition: " #cond ")" << std::endl;
	#define EXIT_PNT(what) \
		std::cerr << "Exit in " << __FUNCTION__ << ": " << what << std::endl;
	#define CHECK_GL_PNT(e, desc) \
		std::cerr << "OpenGL error in function: " << __FUNCTION__ << ", at line: " << __LINE__ << "; GLError{code, desc} = { " << e << ", " << desc << " }" << std::endl;
#endif

/************************ Assert ***************************/
/***********************************************************/
#define ASSERT(cond, what) { \
	if (!(cond)) { \
		if (std::strcmp(what, "") != 0) \
			ASSERT_PNT(cond, what) \
		assert(false); \
	} \
}	

#define EXIT(what) { \
	EXIT_PNT(what) \
	std::exit(1); \
}

#define THROW_ERR(msg) throw std::runtime_error(msg);

/*********************** OpenGL About **********************/
/***********************************************************/
#define CHECK_GL_ERR_MSG(msg) \
{ \
	GLenum e = glGetError(); \
	if (e != GL_NO_ERROR) { \
		const char* desc; \
		switch (e) { \
			case GL_INVALID_OPERATION: desc = "GL_INVALID_OPERATION"; break; \
			case GL_INVALID_ENUM: desc = "GL_INVALID_ENUM"; break; \
			case GL_INVALID_VALUE: desc = "GL_INVALID_VALUE"; break; \
			default: desc = "GL_unknown"; break; \
		} \
		CHECK_GL_PNT(e, desc) \
		if (std::strcmp(msg, "") != 0) EXIT_PNT(msg); \
		std::abort(); \
	} \
}

#define CHECK_GL_ERR CHECK_GL_ERR_MSG("")